/*!
    \copyright  Copyright (c) 2024 - 2025 Qualcomm Technologies International, Ltd.
                All Rights Reserved.
                Qualcomm Technologies International, Ltd. Confidential and Proprietary.
    \file
    @{
        \ingroup    adkfs
        \brief      Provides a consistent API layer over the various ADK filesystems

*/
#include "adkfs_logging.h"
#include "adkfs.h"
#include "adkfs_provider_if.h"
#include "adkfs_types.h"

#include <stdbool.h>
#include <stdlib.h>
#include <string.h>


typedef struct adkfs_fhandle_tag_t {
    void* provider_handle;
    adkfs_provider_if_t* provider;
} adkfs_fhandle_tag_t;


static const char* adkFs_SkipProviderRootPath(const adkfs_provider_if_t* provider, const char *path)
{
    return path + strlen(provider->root_path);
}

int32_t AdkFs_Fopen(adkfs_fhandle_t *handle, const char *path, adkfs_access_flags_t access)
{
    PanicNull(handle);

    adkfs_fhandle_tag_t *adkfs_handle = PanicUnlessNew(adkfs_fhandle_tag_t);
    memset(adkfs_handle, 0, sizeof(*adkfs_handle));

    adkfs_handle->provider = AdkFs_GetProvider(path);

    if (!adkfs_handle->provider)
    {
        DEBUG_LOG_PANIC("Provider not found for path=%s", path);
    }

    int32_t returncode = adkfs_handle->provider->Fopen(&adkfs_handle->provider_handle, adkFs_SkipProviderRootPath(adkfs_handle->provider, path), access);

    if (ADKFS_SUCCESS(returncode))
    {
        *handle = adkfs_handle;
    }
    else
    {
        free(adkfs_handle);
        *handle = NULL;
    }

    return returncode;
}

int32_t AdkFs_Fsize(const char *path, size_t *fsize)
{
    adkfs_provider_if_t* provider = AdkFs_GetProvider(path);
    return provider->Fsize(adkFs_SkipProviderRootPath(provider, path), fsize);
}

int32_t AdkFs_Fdelete(const char *path)
{
    adkfs_provider_if_t* provider = AdkFs_GetProvider(path);
    return provider->Fdelete(adkFs_SkipProviderRootPath(provider, path));
}

int32_t AdkFs_Fmap(adkfs_fhandle_t handle, const void **fbuffer)
{
    return handle->provider->Fmap(handle->provider_handle, fbuffer);
}

int32_t AdkFs_Funmap(adkfs_fhandle_t handle)
{
    return handle->provider->Funmap(handle->provider_handle);
}

int32_t AdkFs_Fseek(adkfs_fhandle_t handle, off_t offset, adkfs_fseek_reference_t ref)
{
    switch(ref)
    {
        case ADKFS_SEEK_SET:
        break;

        case ADKFS_SEEK_CUR:
        {
            off_t cur = 0;
            PanicFalse(handle->provider->Ftell(handle->provider_handle, &cur) == 0);
            offset += cur;
        }
        break;

        case ADKFS_SEEK_END:
        {
            size_t size = 0;
            PanicFalse(handle->provider->Fsize(handle->provider_handle, &size));
            offset = size + offset;
        }
        break;
    }

    PanicFalse(offset >= 0);
    return handle->provider->Fseek(handle->provider_handle, offset);
}

int32_t AdkFs_Ftell(adkfs_fhandle_t handle, off_t *offset)
{
    return handle->provider->Ftell(handle->provider_handle, offset);
}

int32_t AdkFs_Fread(adkfs_fhandle_t handle, void *buf_ptr, size_t read_size, size_t *bytes_read)
{
    return handle->provider->Fread(handle->provider_handle, buf_ptr, read_size, bytes_read);
}

int32_t AdkFs_Fwrite(adkfs_fhandle_t handle, const void *buf_ptr, size_t write_size, size_t *bytes_written)
{
    return handle->provider->Fwrite(handle->provider_handle, buf_ptr, write_size, bytes_written);
}

int32_t AdkFs_Fclose(adkfs_fhandle_t *handle)
{
    int32_t returncode = (*handle)->provider->Fclose((*handle)->provider_handle);
    free(*handle);
    handle = NULL;
    return returncode;
}
/*! @} */
